import * as THREE from 'three'
import { Three } from '../Three/Three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';

export class Apply extends Three {
  constructor(object) {
    super(object)
  }
  test(obj) {
    const material = new THREE.MeshBasicMaterial({
      transparent: true,
      side: THREE.DoubleSide,
      opacity: 0.5,
      color: 0x0642a7,
      linewidth: 1,
      linecap: "round", //ignored by WebGLRenderer
      linejoin: "round", //ignored by WebGLRenderer
      wireframe: true,
    });
    obj.material = material
    this.addGlow(obj)
  }
  /** 
   * 增加一个基础mesh
   * @param position <object> mesh定位
   * @param lng <number> mesh的经度（传入的是经纬度则需要转换）
   * @param lat <number> mesh的纬度（传入的是经纬度则需要转换）
   * @param sideLength <number> mesh边长
   */
  addMesh (config = {}) {
    let { groupName = "scene", position = { x: 0, y: 0, z: 0 }, lng = -1, lat = -1, sideLength = 100, color = 0xff0000, getObject = () => { return false } } = config
    // 没有传入经纬度则直接使用position
    let xyz
    if (lng === -1) {
      xyz = new THREE.Vector3(position.x, position.y, position.z)
    } else {
      xyz = this.lngLatToXyz({ lng, lat })
    }
    let box = new THREE.BoxGeometry(sideLength, sideLength, sideLength)
    let material = new THREE.MeshBasicMaterial({ color, side: THREE.DoubleSide })
    let mesh = new THREE.Mesh(box, material)
    mesh.position.set(xyz.x, xyz.y, xyz.z)
    this[groupName].add(mesh)
    getObject(mesh)
  }
  /** 设置为带有自发光的图层 */
  addGlow (mesh) {
    mesh.layers.enable(10);
  }
  /**
   * 自发光初始化
   */
  glowInit () {
    let { scene, renderer, camera } = this
    const BLOOM_SCENE = 10;
    const bloomLayer = new THREE.Layers();
    bloomLayer.set(BLOOM_SCENE);
    const params = {
      exposure: 1,
      bloomStrength: 0.5,
      bloomThreshold: 0,
      bloomRadius: 0,
      scene: "Scene with Glow"
    };
    // const params = {
    //   exposure: 2,
    //   bloomStrength: 7,
    //   bloomThreshold: 0,
    //   bloomRadius: 1,
    //   scene: "Scene with Glow"
    // };
    const darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });
    const materials = {};
    const renderScene = new RenderPass(scene, camera);
    const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
    bloomPass.threshold = params.bloomThreshold;
    bloomPass.strength = params.bloomStrength;
    bloomPass.radius = params.bloomRadius;
    const bloomComposer = new EffectComposer(renderer);
    bloomComposer.renderToScreen = false;
    bloomComposer.addPass(renderScene);
    bloomComposer.addPass(bloomPass);
    const finalPass = new ShaderPass(
      new THREE.ShaderMaterial({
        uniforms: {
          baseTexture: { value: null },
          bloomTexture: { value: bloomComposer.renderTarget2.texture }
        },
        vertexShader: document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,
        defines: {}
      }), "baseTexture"
    );
    finalPass.needsSwap = true;
    const finalComposer = new EffectComposer(renderer);
    finalComposer.addPass(renderScene);
    finalComposer.addPass(finalPass);
    const render = () => {
      requestAnimationFrame(render)
      scene.traverse((obj) => {
        if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
          materials[obj.uuid] = obj.material;
          obj.material = darkMaterial;
        }
      });
      bloomComposer.render();
      scene.traverse((obj) => {
        if (materials[obj.uuid]) {
          obj.material = materials[obj.uuid];
          delete materials[obj.uuid];
        }
      });
      finalComposer.render();
      this.controls.update()
    }
    render()
  }
  addDirectionalLight () {
    let sunShine = new THREE.DirectionalLight(0xffffff, 2) // 平行光
    sunShine.position.set(this.sunRadius * -1, 0, this.sunRadius * -1)
    sunShine.castShadow = true; // 打开阴影
    sunShine.shadow.mapSize.width = 16384; // default
    sunShine.shadow.mapSize.height = 16384; // default
    sunShine.shadow.camera.left = -15000;
    sunShine.shadow.camera.right = 15000;
    sunShine.shadow.camera.top = 15000;
    sunShine.shadow.camera.bottom = -15000;
    sunShine.shadow.camera.near = -15000; // default
    sunShine.shadow.camera.far = 15000; // default   
    sunShine.name = 'sunShine'
    this.scene.add(sunShine)

  }

  addBase () {
    var renderObject = new THREE.Object3D();
    let url = 'images/test-1.png'
    const textureLoader = new THREE.TextureLoader();
    var map = textureLoader.load(url);
    map.repeat.x = 2;
    var material = new THREE.MeshStandardMaterial({
      transparent: true,
      map,
      opacity: 1,
      depthWrite: false
    });
    var geometry = new THREE.CylinderGeometry(1, 1, 0.5, 50, 1, true);
    geometry.translate(0, -1, 0);
    var mesh = new THREE.Mesh(geometry, material.clone());
    mesh.material.side = THREE.DoubleSide; // front faces
    mesh.renderOrder = 1;
    renderObject.add(mesh);
    this.addGlow(mesh)
    this.scene.add(renderObject)
    const animate = () => {
      mesh.material.opacity -= 0.0025
      renderObject.scale.x += 0.01
      renderObject.scale.z += 0.01
      if (renderObject.scale.x > 5) {
        mesh.material.opacity = 1
        renderObject.scale.x = 0
        renderObject.scale.z = 0
      }
      requestAnimationFrame(animate);
    }
    animate();
  }
  /**
   * 场景内加入漂浮物装饰
   * @param {*} config.sideLength 漂浮物边长
   * @param {*} config.color 颜色
   * @param {*} config.opacity 透明度
   * @param {*} config.length 数量
   */
  addDecorate (config = {}) {
    let {
      sideLength = Math.random() * 0.035 + 0.01,
      color = '#fff',
      opacity = 0.3,
      length = 500
    } = config
    const addPlan = () => {
      let plan = new THREE.PlaneBufferGeometry(sideLength, sideLength)
      let material = new THREE.MeshBasicMaterial({
        color,
        transparent: true,
        opacity,
        side: THREE.DoubleSide
      })
      let box = new THREE.Mesh(plan, material)
      box.position.x = Math.random() * 20 - 10
      box.position.y = Math.random() * 20 - 10
      box.position.z = Math.random() * 20 - 10
      box.rotateY(Math.PI / 180 * parseInt(Math.random() * 180))
      this.scene.add(box)
      let ani = () => {
        requestAnimationFrame(ani)
        if (box.position.y > 10) {
          box.position.y = -10
        }
        box.position.y += 0.01
      }
      ani()
    }
    for (let i = 0; i < length; i++) {
      addPlan()
    }
  }
  /**
   * 为模型添加UV动画
   * @param {*} config.parentName 查找的父对象名称
   * @param {*} config.meshName 查找的对象名称
   * @param {*} config.offsetX UV动画X轴向速度，负数为反向
   * @param {*} config.offsetY UV动画Y轴向速度，负数为反向
   */
  meshUvMove (config) {
    let { parentName = 'scene', meshName = '', offsetX = 0, offsetY = 0 } = config
    let obj = this[parentName].getObjectByName(meshName)
    let handler = (child) => {
      let ani = () => {
        requestAnimationFrame(ani)
        if (offsetX) {
          child.material.map.offset.x += offsetX
        }
        if (offsetY) {
          child.material.map.offset.y += offsetY
        }
      }
      ani()
    }
    this.getMesh(obj, handler)
  }
  /**
   * 将某个物体移动到某个地址
   * @param {*} config 
   */
  movePoint (config) {
    let { parentName = 'otherGroup', meshName, position } = config
    let obj = this[parentName].getObjectByName(meshName)
    if (obj) {
      let { x, y, z } = position
      obj.position.x = x
      obj.position.y = y
      obj.position.z = z
    }
  }
  /**
   * 增加收集、扩散的箭头特效
   */
  addCollect (config) {
    let { fileNames } = config
    for (let i = 0; i < fileNames.length; i++) {
      const fileName = fileNames[i];
      this.addObj({
        fileName,
        beforeAdd: (mesh) => {
          const attList = mesh.children[0].geometry.attributes.position.array
          let tube = this.otherGroup.getObjectByName(fileName)
          if (!tube) {
            let roadConfig = {
              attList,
              name: fileName,
              skin: 'jiantou-red.png',
              speedX: -0.002,
              radius: 1,
              repeatX: 1,
              radialSegments: 3,
              depthTest: false,
              depthWrite: false,
              visible: false,
            }
            this.addTube(roadConfig)
          }
        }
      })
    }
  }
  /**
   * 增加管道水流向的箭头移动特效
   * @param {*} config 
   * @returns 
   */
  addTubeWar (config) {
    let {
      position = { x: -287, y: -9, z: 175 },
      meshName = 'tube-war-point',
      fileNames = ['cityChange/line/tube-1', 'cityChange/line/tube-2-1', 'cityChange/line/tube-2-2'],
      skinList = ['jiantou-1.png', 'jiantou-2-1.png', 'jiantou-2-2.png']
    } = config
    let object = this.otherGroup.getObjectByName(fileNames[0])
    if (object) {
      return
    }
    let obj = this.otherGroup.getObjectByName(meshName)
    let { x, y, z } = position
    obj.children[0].position.x = x
    obj.children[0].position.y = y
    obj.children[0].position.z = z
    for (let i = 0; i < fileNames.length; i++) {
      const fileName = fileNames[i];
      this.addObj({
        fileName,
        beforeAdd: (mesh) => {
          this.addTube({
            name: fileName,
            attList: mesh.children[0].geometry.attributes.position.array,
            radius: 1.5,
            repeatY: 8,
            repeatX: [20, 15, 15][i],
            speedX: -0.01,
            skin: skinList[i],
            beforeAdd () {
              return true
            }
          })
        }
      })
    }
  }
  /**
   * 点位移动效果
   * @param {*} config.meshName 需要移动的模型名称
   * @param {*} config.fileName 移动路线文件名称
   * @param {*} config.length 移动点位数组长度（可以用其控制速度，长度越长速度越慢）
   * @param {*} config.pathName 移动路线模型名称
   */
  pointMove (config) {
    let { ischild = true, newWindow = 10, meshName, fileName, length = 500, pathName = '', radius, skin, repeatX, radialSegments, beforeMove = () => { } } = config
    this.addObj({
      fileName,
      beforeAdd: (mesh) => {
        let points = this.att2Points(mesh.children[0].geometry.attributes.position.array)
        let list = new THREE.CatmullRomCurve3(points, false, 'catmullrom', 0)
        let splitPoints = list.getPoints(length);
        let obj = this.otherGroup.getObjectByName(meshName)
        console.log(obj)
        let index = 0
        let ani = () => {
          if (index !== splitPoints.length - 1) {
            if (index === parseInt(splitPoints.length / newWindow) - 2) {
              beforeMove(splitPoints[index])
            }
            index++
            requestAnimationFrame(ani)
            let { x, y, z } = splitPoints[index]
            if (ischild) {
              obj.children[0].position.x = x
              obj.children[0].position.y = y
              obj.children[0].position.z = z
            } else {
              obj.position.x = x
              obj.position.y = y
              obj.position.z = z
            }
          }
        }
        ani()
        let tube = this.otherGroup.getObjectByName(pathName)
        if (!tube) {
          let roadConfig = {
            points: splitPoints,
            name: pathName,
            skin,
            speedX: -0.002,
            radius,
            repeatX,
            radialSegments,
            depthTest: false,
            depthWrite: false,
          }
          this.addTube(roadConfig)
        }
      }
    })

  }
  /**
   * 相机漫游
   */
  roam ({ fileName }) {
    this.addObj({
      fileName,
      beforeAdd: (mesh) => {
        this.cameraRoam = true
        let atts = mesh.children[0].geometry.attributes.position.array
        let points = this.att2Points(atts)
        let curve = new THREE.CatmullRomCurve3(points, true, 'catmullrom', 0.01);
        let evenPoints = curve.getPoints(4000);
        this.roamPath = evenPoints
        return false
      }
    })
  }
  /**
   * 线框化材质
   * @param {*} param.meshName 需要转换成线框材质的模型名称
   * @param {*} param.isGlow 是否需要自发光
   * @param {*} param.object3d 需要转换成线框材质的模型对象
   * @param {*} param.opacity 线框透明度
   * @param {*} param.color 线框颜色
  */
  toWireframe (config = {}) {
    let { scale = 1, copyName = '', type = "replace", isGlow = false,
      object3d = null, meshName, opacity = 0.1, color = 0x93f0fc } = config
    let object
    if (object3d) {
      object = object3d
    } else {
      object = this.scene.getObjectByName(meshName)
    }
    let newObject3d, fatherObject
    if (type === "copy") {
      newObject3d = new THREE.Group()
      newObject3d.copy(object)
      newObject3d.scale.set(scale, scale, scale)
      fatherObject = new THREE.Group()
      fatherObject.name = copyName
      fatherObject.visible = true
      fatherObject.add(newObject3d)
      this.otherGroup.add(fatherObject)
    } else {
      // fatherObject = new THREE.Group()
      newObject3d = object
      newObject3d.name = copyName
      // newObject3d.name = copyName
      // newObject3d.scale.set(scale, scale, scale)
      // newObject3d.visible = true
      this.otherGroup.add(newObject3d)
      // fatherObject.add(object)
      // this.otherGroup.add(fatherObject)
    }
    const detail = (child) => {
      if (child && child.material) {
        if (isGlow) {
          this.addGlow(child)
        }
        const material = new THREE.MeshBasicMaterial({
          transparent: true,
          // side: THREE.DoubleSide,
          opacity,
          color,
          // linewidth: 1,
          // linecap: 'round', //ignored by WebGLRenderer
          // linejoin: 'round', //ignored by WebGLRenderer
          // depthWrite: false,
          // depthTest: false,
          // wireframe: true
        });
        child.material = material
        this.flicker(child)
      }
    }
    this.getMesh(newObject3d, detail)
  }
  /**
   * 路网初始化
   * @param {*} config 
   */
  roadInit (config) {
    let { radius, fileName, groupName, skin, repeatY = 1, repeatX = 1, speedX, speedY, visible = false, offset = { x: 0, y: 0, z: 0 } } = config
    this.addObj({
      fileName,
      beforeAdd: (obj) => {
        for (let i = 0; i < obj.children.length; i++) {
          const attList = obj.children[i].geometry.attributes.position.array
          this.addTube({ radius, offset, attList, skin, repeatY, repeatX, height: 8, name: groupName, speedX, speedY, visible })
        }
      }
    })
  }
  /** 相机飞行 */
  cameraFly (params) {
    this.flyTo(params)
  }
  /** 替换水波材质 */
  setWater (waterObject) {
    this.addWater(waterObject)
  }
  /**
   * 添加飞线
   * @param object.lineList 弧线数组，包含起点坐标和终点坐标
   * @param object.name 弧线组名称
   * @param object.height 弧线高度
   */
  addFlyLine ({ lineList, height, name = '' }) {
    let group = new THREE.Group()
    group.visible = false
    group.name = name
    this.otherGroup.add(group)
    for (let i = 0; i < lineList.length; i++) {
      const element = lineList[i];
      this.addCurve3({
        height,
        startPoint: element.startPoint,
        endPoint: element.endPoint,
        beforeAdd: mesh => {
          group.add(mesh)
        }
      })
    }
  }
  /** 添加电子围栏 
   * @param config.type 电子围栏类型，目前只有buffer（垂直墙）和tube（管道）两种 
   * @param config.visible 初始化时显影状态
   * @param config.groupName 控制显影需要用到的名称
   * @param config.radius 管道类型的管道半径
   * @param config.tension 管道类型的管道弯曲张力
   * @param config.radialSegments 管道类型的截面面数
   * @param config.attsList 顶点坐标数据（attributes）类型
   * @param config.skin 贴图文件名称
   * @param config.repeatX 管道类型的贴图X轴向复制数量，垂直墙类型光环的X轴向复制数量
   * @param config.repeatY 管道类型的贴图Y轴向复制数量，垂直墙类型光环的Y轴向复制数量
   * @param config.speedX 光环的X轴向移动速度
   * @param config.speedY 光环的Y轴向移动速度
   * @param config.haloFileName 光环贴图文件名称
   * @param config.pointsList 顶点坐标数据（Vector3）类型
   * @param config.height 垂直墙类型高度
   * @param config.opacity 围栏透明度
   * @param config.topColor 垂直墙类型顶部颜色（到底部呈渐变色，目前只支持rgba颜色格式）
   * @param config.bottomColor 垂直墙类型底部颜色（目前只支持rgba颜色格式）
  */
  addEleFen (config) {
    if (config.type === 'buffer') {
      this.addElectronicFenceFormBuf(config)
    }
    if (config.type === 'tube') {
      this.addElectronicFenceFormTube(config)
    }
  }
  /**
   * 加入区域面
   * @param object.height 区域面高度
   * @param object.positionList 多个区域面坐标集合，数组内的item为Xyz格式
   * @param object.color 区域面颜色
   * @param object.opacity 区域面透明度
   * @param object.name 区域面名称
   */
  addArea (object) {
    if (object && object.positionList) {
      let group = new THREE.Group()
      group.name = object.groupName
      group.visible = false
      this.otherGroup.add(group)
      let height = object && object.height ? object.height : 50
      let opacity = object.opacity || 1
      let positionList = object.positionList
      for (let j = 0; j < positionList.length; j++) {
        const points = positionList[j];
        const shape = new THREE.Shape();
        shape.moveTo(points[0].x, points[0].z);
        for (let i = 1; i < points.length; i++) {
          shape.lineTo(points[i].x, points[i].z);
        }
        const extrudeSettings = {
          steps: 0,
          depth: height,
          bevelEnabled: false,
          bevelThickness: 0,
          bevelSize: 0,
          bevelOffset: 0,
          bevelSegments: 0
        };

        let TextureLoader = new THREE.TextureLoader()
        let texture = TextureLoader.load('images/lightPoint.png')
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set(0.03, 0.03)

        let material = new THREE.MeshLambertMaterial({ map: texture, side: 0, transparent: true, opacity });
        const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
        const mesh = new THREE.Mesh(geometry, material);
        mesh.rotateX(Math.PI / 2)
        mesh.position.y = height
        group.add(mesh)

        const linetexture = TextureLoader.load('images/line7.png')
        linetexture.wrapS = linetexture.wrapT = THREE.RepeatWrapping;
        linetexture.repeat.set(1, 1)
        const linematerial = new THREE.MeshLambertMaterial({ map: linetexture, side: 2, transparent: true, opacity });
        const curve = new THREE.CatmullRomCurve3(this.toThreePoints(points), true, 'catmullrom', 0)
        const linegeometry = new THREE.TubeGeometry(curve, 100, 8, 8, true)
        const line = new THREE.Mesh(linegeometry, linematerial);
        line.position.y = height
        group.add(line);
      }
    } else {
      console.log('未传入区域的坐标数组')
    }
  }
}
